﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Nemerle.Imperative;
using System.IO;
using System.Security.Cryptography;
using System.Windows.Forms;

namespace FTClient
{
	class ThreadedServer
    {
        public mutable _serverSocket:Socket;
        private mutable _port:int;
        private mutable _ip:IPAddress;

        public this(ip:IPAddress, port:int)
            { 
                _port = port; 
                _ip=ip;
            }

    
    
    public fileList:List[oneFileInfo]=List();
    
    
        
    private mutable _acceptThread:Thread;
    
    private _connections:List[ConnectionInfo] =List();
    
    private GetFilePath(hash:int):string
    {
        foreach(fileInfo:oneFileInfo in fileList)
        {
            when (fileInfo.hash==hash)
                return fileInfo.filepath;
        }
        "test.txt";
    }
    
    private GetFileHash(FilePath:string):int
    {
        // This is one implementation of the abstract class MD5.
        def md5:MD5 = MD5CryptoServiceProvider();
        mutable fileHash=0;
        using (fs = File.OpenRead(FilePath)) 
        {
            def hash=md5.ComputeHash(fs);
            fileHash=hash[0]+hash[1]*255+hash[2]*255*255;
        }
        fileHash;


    }
    public AddFile(FileName:string, FilePath:string):void
    {
        mutable oneFile=oneFileInfo();
        oneFile.filename=FileName;
        oneFile.filepath=FilePath;
        oneFile.hash=GetFileHash(FilePath);
        fileList.Add(oneFile);
    }

    public Start():void
    {
        SetupServerSocket();
        _acceptThread = Thread(AcceptConnections);
        _acceptThread.IsBackground = true;
        _acceptThread.Start();
        fileList.Clear();
    }
    public Stop():void
    {
        _serverSocket.Close();
        _acceptThread.Abort();
    }

    private SetupServerSocket():void
    {
        mutable hostEntry:IPHostEntry = null;
        
        
        try
        {
            // Get host related information.        
            hostEntry = Dns.GetHostEntry(Dns.GetHostName()):IPHostEntry;
            
            mutable address=_ip;//IPAddress.Parse("127.0.0.1");
            
//            foreach(address:IPAddress in hostEntry.AddressList)
//            {
                mutable ipe = IPEndPoint(address, _port);
                _serverSocket = Socket(ipe.Address.AddressFamily,SocketType.Stream, ProtocolType.Tcp);
                _ = _serverSocket.Bind(ipe);
                _ = _serverSocket.Listen(100);
//            }
        }
         catch
        {
           | e => _ = MessageBox.Show($"Some Exception: $(e.Message)");

        }
/*        
        // Получаем информацию о локальном компьютере
        def localMachineInfo:IPHostEntry  = Dns.GetHostEntry(Dns.GetHostName()):IPHostEntry;
        def myEndpoint:IPEndPoint =IPEndPoint(//IPAddress.Parse("127.0.0.1"),_port);
            localMachineInfo.AddressList[0], _port);

        // Создаем сокет, привязываем его к адресу
        // и начинаем прослушивание
        _serverSocket = Socket(
            myEndpoint.Address.AddressFamily,
            SocketType.Stream, ProtocolType.Tcp);
        _ = _serverSocket.Bind(myEndpoint);
        _ = _serverSocket.Listen(100);
*/
    }

    private AcceptConnections():void
    {
        while (true)
        {
            // Принимаем соединение
            def socket = _serverSocket.Accept();
            def connection = ConnectionInfo();
            connection.Socket = socket;

            // Создаем поток для получения данных
            connection.Thread = Thread(ProcessConnection);
            connection.Thread.IsBackground = true;
            connection.Thread.Start(connection);

            // Сохраняем сокет
            lock (_connections) _connections.Add(connection);
        }
    }

    

    private ProcessConnection(state:object):void
    {
        def connection = state:>ConnectionInfo;
        def buffer:array[byte] = array(256);
        try
        {
            mutable read:int=0;       
            mutable hash=0;
            mutable uid="";
            while (read<1)
            {
                mutable buffer:array[Byte]=array(256);
	            _ = connection.Socket.Receive(buffer);
	            uid=Encoding.ASCII.GetString(buffer).Trim('\0');
                connection.Socket.Send(buffer);
                def hashBuf:array[byte] = array(3);
                def bytesRead:int = connection.Socket.Receive(hashBuf);
                if (bytesRead > 0)
                {
                    when(hash==0)
                    {
                        hash=hashBuf[0]+hashBuf[1]*255+hashBuf[2]*255*255;
                    }
                    when (!GlobalServer.VerifyDownload(hash,uid))
                    {
                        buffer[0]=0;
                        _ = connection.Socket.Send(buffer);
                        break;
                    }
                    def filepath = GetFilePath(hash);// FindFile(filename);
                    
                    using (fs = File.OpenRead(filepath)) 
                    {
                        def readbuf:array[byte] = array(255);
                        mutable num=fs.Read(readbuf,0,readbuf.Length):>Byte;
                        mutable lastnum=0;
                        while (num > 0) 
                        {
                            buffer[0]=num;
                            for (mutable i=1;i<256;i++)
                                buffer[i]=readbuf[i-1];
                            _ = connection.Socket.Send(buffer);
                            lastnum=num;
                            num=fs.Read(readbuf,0,readbuf.Length):>Byte;
                            
                        }
                        when (lastnum==255)
                        {
                            buffer[0]=0;
                            _ = connection.Socket.Send(buffer);
                        }
                        _ = fs.Close();
   
                    }
                    
                    
                }
                else when (bytesRead == 0) read=2;
            }
        }
        catch
        {
           | e => _ = MessageBox.Show($"Some Exception: $(e.Message)");

        }
        finally
        {
            _ = connection.Socket.Close();
            lock (_connections) _connections.Remove(
                connection);
        }
    }
}
}
